package br.com.transitojp.dao;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.criterion.Criterion;

/**  
 * GenericDAOHibernate.java - a Generic DAO that the other DAOs extend.  
 * @author LAViD - Laboratório de Aplicação de Vídeo Digital.
 * @version 1.0 
 */
public abstract class GenericDAOHibernate<T, ID extends Serializable> implements IGenericDAOHibernate<T, ID> {

	private Class<T> persistentClass;
	private Session session;

	/**  
	 * The purpose of this constructor it's to set the session and the persistentClass.  
	 * @param session A variable of type Session.  
	 */
	@SuppressWarnings("unchecked")
	public GenericDAOHibernate(Session session) {
		this.persistentClass = (Class<T>) ((ParameterizedType) getClass()
				.getGenericSuperclass()).getActualTypeArguments()[0];
		this.session = session;
	}

	/**  
	 * The purpose of this method is to set the value of the variable session.  
	 * @param s A variable of type Session.
	 * @return no return.  
	 */
	public void setSession(Session s) {
		this.session = s;
		
	}

	/**  
	 * The purpose of this method is to retrieve the value of the variable session.  
	 * @return A Session data type.  
	 */
	public Session getSession() {
		if (session == null)
			throw new IllegalStateException("Session has not been set on DAO before usage");
		return session;
	}

	/**  
	 * The purpose of this method is to retrieve the value of the variable persistentClass.
	 * @return A Class(T) data type.  
	 */
	public Class<T> getPersistentClass() {
		return persistentClass;
	}

	/**  
	 * The purpose of this method is to find on the DB an Object through it's id.  
	 * @param id A variable of type ID.
	 * @return A T data type.  
	 */
	@SuppressWarnings("unchecked")
	public T findById(ID id) {
		return (T) getSession().load(getPersistentClass(), id);
	}

	/**  
	 * The purpose of this method is to find an all the Objects on the DB that the type matches T.  
	 * @return A List(T) data type.  
	 */
	public List<T> findAll() {
		return findByCriteria();
	}

	/**  
	 * The purpose of this method is to persist an Object on the DB.  
	 * @param entity A variable of type T.
	 * @return A T data type.  
	 */
	public T makePersistent(T entity) {
		getSession().saveOrUpdate(entity);
		return entity;
	}

	/**  
	 * The purpose of this method is to find delete an Object from the DB.  
	 * @param entity A variable of type T.
	 * @return no return.  
	 */
	public void makeTransient(T entity) {
		getSession().delete(entity);
	}

	/**  
	 * The purpose of this method is to execute whatever is in the queue on the moment.  
	 * @return no return.  
	 */
	public void flush() {
		getSession().flush();
	}

	/**  
	 * The purpose of this method is to clear the Session.  
	 * @return no return.  
	 */
	public void clear() {
		getSession().clear();
	}

	/**  
	 * The purpose of this method is be used inside subclasses as a convenience method.  
	 * @param criterion A variable of type Criterion.
	 * @return A List(T) data type.  
	 */
	@SuppressWarnings("unchecked")
	protected List<T> findByCriteria(Criterion... criterion) {
		Criteria crit = getSession().createCriteria(getPersistentClass());
		for (Criterion c : criterion) {
			crit.add(c);
		}
		return crit.list();
	}

}